/*
 * Copyright (C) 2006, 2007, 2008, 2009, 2010 Apple, Inc.  All rights reserved.
 * Copyright (C) 2010 Patrick Gansterer <paroga@paroga.com>
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"
#include "WebView.h"
#include "view/viewgroup.h"
#include "view/viewoverlay.h"
#include "widget/textview.h"
#include "widget/framelayout.h"
#include "EmptyClients.h"
#include "ChromeClientCd.h"
#include "ContextMenuClientCd.h"
#include "DragClientCd.h"
#include "EditorClientCd.h"
#include "EventHandler.h"
#include "FocusController.h"
#include "Frame.h"
#include "FrameLoadRequest.h"
#include "FrameLoader.h"
#include "FrameLoaderClientCd.h"
#include "FrameView.h"
#include "PlatformEvent.h"
#include "PlatformTouchEvent.h"
#include "PlatformMouseEvent.h"
#include "RenderTreeAsText.h"
#include "GraphicsContext.h"
#include "EmptyClients.h"
#include "HTMLFormElement.h"
#include "InitializeThreading.h"
#include "InspectorClientCd.h"
#include "IntSize.h"
#include "MainFrame.h"
#include "NotImplemented.h"
#include "Page.h"
#include "PlatformKeyboardEvent.h"
#include "PlatformMouseEvent.h"
#include "PlatformStrategiesCd.h"
#include "PlatformWheelEvent.h"
#include "ProgressTrackerClientCd.h"
#include "GeolocationClientCd.h"
#include "mock/GeolocationClientMock.h"
#include "ResourceRequest.h"
#include "Settings.h"
#include "SharedBuffer.h"
#include <wtf/MainThread.h>
#include <wtf/RunLoop.h>
#include <wtf/CurrentTime.h>
#include "WebKitLogging.h"
#include "MemoryPressureHandler.h"

using namespace WebCore;
namespace WebKit{
PassRefPtr<SharedBuffer> loadResourceIntoBuffer(const char* name){
    notImplemented();
    return 0;
}

void initWebkit(){
    static int inited=0;
    if(inited)
	 return;
    inited++;
    RunLoop::initializeMainRunLoop();
    JSC::initializeThreading();
    WTF::initializeMainThread();
#if !LOG_DISABLED
    WebKitInitializeLoggingChannelsIfNecessary();
#endif    
    PlatformStrategiesCd::initialize();

    auto& memoryPressureHandler = MemoryPressureHandler::singleton();
    memoryPressureHandler.setShouldUsePeriodicMemoryMonitor(true);
    memoryPressureHandler.setLowMemoryHandler([] (/*Critical critical,*/ bool synchronous) {
	    //WTF::releaseFastMallocFreeMemory();
	    //WebCore::releaseMemory(critical, synchronous);
	    printf("%s:%s LowMemoryHandler is called\r\n",__FILE__,__FUNCTION__);
    });
    memoryPressureHandler.install();   
}

WebView::WebView(int w,int h)
    : ViewGroup(w,h) , m_page(nullptr)
{
    Page::PageClients pageClients;
    initWebkit();
    setFocusable((int)View::FOCUSABLE_AUTO);
    setFocusableInTouchMode(true);
    pageClients.chromeClient = new WebKit::ChromeClientCd(this);
    pageClients.editorClient = new WebKit::EditorClientCd(this);
    pageClients.dragClient   = new WebKit::DragClientCd();
#if ENABLE(CONTEXT_MENUS)    
    pageClients.contextMenuClient= new WebKit::ContextMenuClientCd(this);
#endif    
#if ENABLE(INSPECTOR)    
    pageClients.inspectorClient  = new WebKit::InspectorClientCd(this);
#endif 
#if ENABLE(GEOLOCATION)
    WebCore::GeolocationClientMock*mockClient= new WebCore::GeolocationClientMock();
    pageClients.geolocationClient = mockClient;//new WebKit::GeolocationClientCd(this);
#endif    
    pageClients.progressTrackerClient = new WebKit::ProgressTrackerClientCd();

    WebKit::FrameLoaderClientCd* loaderClient = new WebKit::FrameLoaderClientCd(this,(WebCore::Frame*)1);
    pageClients.loaderClientForMainFrame  = loaderClient;

    m_page = new Page(pageClients);
    unsigned layoutMilestones = DidFirstLayout | DidFirstVisuallyNonEmptyLayout;
    m_page->addLayoutMilestones(static_cast<LayoutMilestones>(layoutMilestones));
#if ENABLE(CLIENT_BASED_GEOLOCATION)
    LOG(WebView,"geolocationController=%p",m_page->geolocationController());
    mockClient->setController(m_page->geolocationController());
    mockClient->setPosition(GeolocationPosition::create(currentTime(),.0,.0,.0));// latitude, longitude, accuracy)); 
#endif
    MainFrame& main_frame=m_page->mainFrame();
    loaderClient->setFrame(&main_frame);
    main_frame.init();
    LOG(WebView,"WebView::WebView %p page=%p mainframe=%p",this,m_page,&main_frame);

    Settings& settings = main_frame.settings();
    settings.setDefaultFixedFontSize(14);
    settings.setDefaultFontSize(14);
    settings.setMinimumFontSize(8);
    settings.setMinimumLogicalFontSize(8);
    settings.setScriptEnabled(true);
    settings.setLoadsImagesAutomatically(true);
    settings.setUsesPageCache(true);
    settings.setOfflineWebApplicationCacheEnabled(true);

    settings.setAllowUniversalAccessFromFileURLs(true);
    settings.setAllowFileAccessFromFileURLs(true);
    settings.setJavaScriptCanAccessClipboard(true);
    settings.setShouldPrintBackgrounds(false);
    settings.setTextAreasAreResizable(true);
    settings.setLocalStorageEnabled(true);
    settings.setAcceleratedCompositingEnabled(false);
    //settings.setSupportMultipleWindows(true);

    m_page->focusController().setActive(true);
    m_page->focusController().setFocusedFrame(&main_frame);
}

WebView::~WebView(){
    m_page=nullptr;
}

void WebView::setContentSize(int horz,int vert){
    m_ContentSize = WebCore::IntSize(horz,vert);
    setHorizontalScrollBarEnabled(horz>getWidth());
    setVerticalScrollBarEnabled(vert>getHeight());
}

int WebView::computeHorizontalScrollRange(){
    return std::max(getWidth(),m_ContentSize.width());
}

int WebView::computeVerticalScrollRange(){
    return std::max(getHeight(),m_ContentSize.height());
}

PassRefPtr<Frame> WebView::createFrame(const URL& url, const String& name, HTMLFrameOwnerElement* ownerElement, 
   const String& referrer,bool /*allowsScrolling*/, int /*marginWidth*/, int /*marginHeight*/, Frame* parentFrame)
{
    WebKit::FrameLoaderClientCd *loaderClient = new WebKit::FrameLoaderClientCd(this,nullptr);
    RefPtr<Frame> childFrame = Frame::create(m_page, ownerElement, loaderClient);
    loaderClient->setFrame(childFrame.get());

    parentFrame->tree().appendChild(childFrame);
    childFrame->tree().setName(name);
    childFrame->init();

    // The creation of the frame may have run arbitrary JavaScript that removed it from the page already.
    if (!childFrame->page())
        return 0;

    parentFrame->loader().loadURLIntoChildFrame(url, referrer, childFrame.get());

    // The frame's onload handler may have removed it from the document.
    if (!childFrame->tree().parent())
        return 0;

    return childFrame.release();
}

void WebView::runJavaScriptAlert(const String& message){
    notImplemented();
}

bool WebView::runJavaScriptConfirm(const String& message){
    notImplemented();
    return false;
}

bool WebView::runJavaScriptPrompt(const String& message, const String& defaultValue, String& result){
    notImplemented();
    return false;
}

void WebView::frameRect(IntRect& rect) const{
    RECT r=getBound();
    rect = IntRect(r.left,r.top,r.width,r.height);
}

void WebView::loadUrl(const WTF::String &url){
    ResourceRequest request(url);
    LOG(WebView,"WebView::loadUrl %s",url.utf8().data());
    load(request);
}

void WebView::load(const WebCore::ResourceRequest &request){
    MainFrame& frame = m_page->mainFrame();
    FrameLoadRequest frmrq(&frame, request);
    frame.loader().load(frmrq);
}

void WebView::reload(){
    WebCore::Frame& frame = m_page->mainFrame();
    frame.loader().reload();
}

void WebView::stop(){
    WebCore::Frame& frame = m_page->mainFrame();
    frame.loader().stopAllLoaders();
}

#define TOOLTIP_ID 100
void WebView::setToolTip(const WTF::String&toolTip){
    m_ToolTip=toolTip;
    cdroid::ViewGroup*overlay=getOverlay()->getOverlayView();
    cdroid::TextView*tv=nullptr;
    if(overlay->getChildCount()==0){
	cdroid::FrameLayout*fl=new cdroid::FrameLayout(0,0);
	overlay->addView(fl);
	tv=new cdroid::TextView(0,0);
	fl->addView(tv).setId(TOOLTIP_ID);
	fl->setBackgroundColor(0x80FF0000);
    }else{
	tv=(cdroid::TextView*)overlay->findViewById(TOOLTIP_ID);
    }
    tv->setText(toolTip.utf8().data());
}

void WebView::setToolTipArea(const WebCore::IntRect&rect){
    m_ToolTipArea=rect;
    cdroid::ViewGroup*overlay=getOverlay()->getOverlayView();
    cdroid::TextView*tv=(cdroid::TextView*)overlay->findViewById(TOOLTIP_ID);
    if(tv&&m_ToolTip.length()){
	 overlay->layout(rect.x(),rect.y(),rect.width(),rect.height());
	 LOG(WebView,"***ToolTip(%d,%d-%d,%d)%s",rect.x(),rect.y(),rect.width(),rect.height(),m_ToolTip.utf8().data());
    }
}

void WebView::onDraw(Canvas&canvas){
    if (m_page==nullptr)return;
    Frame& frame = m_page->mainFrame();
    frame.view()->updateLayoutAndStyleIfNeededRecursive();
    double x1,y1,x2,y2;
    canvas.get_clip_extents(x1,y1,x2,y2);
    GraphicsContext gc(canvas.cobj());
    gc.save();
    gc.applyDeviceScaleFactor(m_page->deviceScaleFactor());
    WebCore::FloatRect rect=WebCore::FloatRect::narrowPrecision(x1,y1,x2-x1,y2-y1);
    //LOG(WebView,"WebView::onDraw rect(%.f,%.f,%.f,%.f)",x1,y1,x2-x1,y2-y1);
    frame.view()->paint(&gc, WebCore::enclosingIntRect(rect));
    gc.restore(); 
}

void WebView::onSizeChanged(int w,int h,int oldw,int oldh){
    LOG(WebView,"WebView::onSizeChanged(%dx%d->%dx%d",oldw,oldh,w,h);
    m_page->mainFrame().view()->resize(w,h);
}

bool WebView::onTouchEvent(cdroid::MotionEvent&event){
    static LONG globalClickCount;
    static IntPoint globalPrevPoint;
    static MouseButton globalPrevButton;
    static LONG globalPrevMouseDownTime;
    const int SLOP = ViewConfiguration::get(nullptr).getScaledDoubleTapTouchSlop();

    PlatformMouseEvent mouseEvent;//(hWnd, message, wParam, lParam);

    bool handled = false;
#if ENABLE(TOUCH_EVENTS)
    event.offsetLocation(-mScrollX,-mScrollY);
    PlatformTouchEvent touchEvent = PlatformTouchEvent(event); 
    event.offsetLocation(mScrollX,mScrollY);
    //handled=m_page->mainFrame().eventHandler().handleTouchEvent(touchEvent);
    //return 1;
#endif	
    WebCore::IntPoint pos;
    pos.setX( event.getY() - mScrollY);
    pos.setY( event.getY() - mScrollY);
    const int action = event.getAction();
    const bool insideThreshold = std::abs(globalPrevPoint.x() - pos.x()) < SLOP
                    && std::abs(globalPrevPoint.y() - pos.y()) < SLOP;
    mouseEvent=PlatformMouseEvent(event);
    switch(action){
    case MotionEvent::ACTION_DOWN:
         mouseEvent.setClickCount(0);
         m_page->mainFrame().eventHandler().handleMouseMoveEvent(mouseEvent);
         if (insideThreshold && mouseEvent.button() == globalPrevButton)
            globalClickCount++;
         else // Reset the click count.
            globalClickCount = 1;
         globalPrevMouseDownTime = event.getEventTime();
         globalPrevButton = mouseEvent.button();
         globalPrevPoint = pos;
         mouseEvent=PlatformMouseEvent(event);
         mouseEvent.setClickCount(globalClickCount);
         handled=m_page->mainFrame().eventHandler().handleMousePressEvent(mouseEvent);
	 break;
    case MotionEvent::ACTION_UP:
         globalPrevButton = mouseEvent.button();
         globalPrevPoint = pos;
         mouseEvent.setClickCount(globalClickCount);
         handled=m_page->mainFrame().eventHandler().handleMouseReleaseEvent(mouseEvent);
	 break;
    case MotionEvent::ACTION_MOVE:
         if (!insideThreshold)
            globalClickCount = 0;
         mouseEvent.setClickCount(globalClickCount);
         handled = m_page->mainFrame().eventHandler().mouseMoved(mouseEvent);
	 break;
    default:break;	    
    }
    return handled||1;
}

/*bool WebView::handleMouseWheel(HWND hWnd, WPARAM wParam, LPARAM lParam, bool isHorizontal){
    PlatformWheelEvent wheelEvent(hWnd, wParam, lParam, isHorizontal);
    return frame()->eventHandler().handleWheelEvent(wheelEvent);
}*/

bool WebView::onKeyDown(int keycode,cdroid::KeyEvent&event){
    Frame& frame = m_page->focusController().focusedOrMainFrame();

    PlatformKeyboardEvent keyEvent(event);//(m_windowHandle, virtualKeyCode, keyData, PlatformEvent::RawKeyDown, systemKeyDown);
    bool handled = frame.eventHandler().keyEvent(keyEvent);
    printf("onKeyDown handled=%d\r\n",handled);
    // These events cannot be canceled, and we have no default handling for them.
    // FIXME: match IE list more closely, see <http://msdn2.microsoft.com/en-us/library/ms536938.aspx>.
    return true;
}

bool WebView::onKeyUp(int keyCode,cdroid::KeyEvent&event){
    ScrollDirection dir;
    ScrollGranularity gran;
    PlatformKeyboardEvent keyEvent(event);
    Frame& frame = m_page->focusController().focusedOrMainFrame();
    bool handled=frame.eventHandler().keyEvent(keyEvent);
    printf("onKeyDown handled=%d\r\n",handled);
    return handled;
    switch(event.getKeyCode()){
    case KEY_PAGE_DOWN: scrollBy(0,getHeight()) ; 
			gran=ScrollByPage;	dir=ScrollDown;
			break;
    case KEY_PAGE_UP  : scrollBy(0,-getHeight()); 
			gran=ScrollByPage;	dir=ScrollUp;
			break;
    case KEY_DPAD_UP  : scrollBy(0,-getHeight()/10);
			gran=ScrollByLine;	dir=ScrollUp;
			break;
    case KEY_DPAD_DOWN: scrollBy(0,getHeight()/10) ;
			gran=ScrollByLine;	dir=ScrollDown;
			break;
    case KEY_DPAD_LEFT: scrollBy(-50,0);
			gran=ScrollByLine;	dir=ScrollLeft;
			break;
    case KEY_DPAD_RIGHT:scrollBy(50,0) ;
			gran=ScrollByLine;	dir=ScrollRight;
			break;
    }
    LOG(WebView,"gran=%d dir=%d",gran,dir);
    if (frame.eventHandler().scrollOverflow(dir, gran))
	return true;
    frame.view()->scroll(dir, gran);
    return true;
}
}//endof namespace
